/* 
 * Copyright (c) 2004-2005 Massachusetts Institute of Technology. This code was
 * developed as part of the Haystack (http://haystack.lcs.mit.edu/) research 
 * project at MIT. Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation files (the 
 * "Software"), to deal in the Software without restriction, including without 
 * limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following conditions: 
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software. 
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 */

package edu.mit.csail.relo.graph;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.LayoutManager;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.eclipse.gef.editpolicies.LayoutEditPolicy;
import org.eclipse.gef.editpolicies.NonResizableEditPolicy;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.CreateRequest;

import edu.mit.csail.relo.parts.ArtifactEditPart;


public class GraphLayoutEditPolicy extends LayoutEditPolicy {

    @Override
    protected EditPolicy createChildEditPolicy(EditPart child) {
        return new NonResizableEditPolicy();
    }
    
    @Override
    public Command getCommand(Request request) {
    	if (REQ_RESIZE_CHILDREN.equals(request.getType()))
    		return getMoveChildrenCommand(request);
    	
        return super.getCommand(request);
    }
    @Override
    protected Command getCreateCommand(CreateRequest request) {
        return null;
    }
    
    @Override
    protected Command getDeleteDependantCommand(Request request) {
        return null;
        //System.err.println("x");
        //return new Command() {};
    }
    @Override
    protected Command getOrphanChildrenCommand(Request request) {
        return getMoveChildrenCommand(request);
    }
    
    @Override
    protected Command getMoveChildrenCommand(Request request) {
        ChangeBoundsRequest req = (ChangeBoundsRequest) request;
        List<AbstractGraphicalEditPart> moveEP = getAGEPTypedList(req.getEditParts());
        
        // we store origBounds because it might be needed for undoing
        Map<AbstractGraphicalEditPart, Rectangle> origBounds = new HashMap<AbstractGraphicalEditPart, Rectangle>(moveEP.size());
        for (AbstractGraphicalEditPart ep : moveEP) {
            origBounds.put(ep, ep.getFigure().getBounds().getCopy());
        }
        
        return new MoveCommand(moveEP, origBounds, req.getMoveDelta());
    }	

    @SuppressWarnings("unchecked")
    private final List<AbstractGraphicalEditPart> getAGEPTypedList(List in) {
        return in;
    }

    public static class MoveCommand extends Command {
        private final List<AbstractGraphicalEditPart> moveEP;
        private final Map<AbstractGraphicalEditPart, Rectangle> origBounds;
        private final Point moveDelta;

        public MoveCommand(List<AbstractGraphicalEditPart> moveEP, Map<AbstractGraphicalEditPart, Rectangle> origBounds, Point moveDelta) {
            super("Move Nodes");
            this.moveEP = moveEP;
            this.origBounds = origBounds;
            this.moveDelta = moveDelta;
        }
        @Override
        public void execute() {
            //performUndoableLayout();
            for (AbstractGraphicalEditPart ep : moveEP) {
                moveEP(ep, origBounds.get(ep), moveDelta);
            }
        }
        @Override
        public void undo() {
            //performUndoableLayout();
            Point undoMoveDelta = moveDelta.getNegated();
            for (AbstractGraphicalEditPart ep : moveEP) {
                Rectangle currentBounds = origBounds.get(ep).getCopy().translate(moveDelta);
                moveEP(ep, currentBounds, undoMoveDelta);
            }
        }

        private void moveEP(AbstractGraphicalEditPart ep, Rectangle startBounds, final Point moveDelta) {
            Point oldLoc = startBounds.getCenter();
            
            Rectangle endBounds = startBounds.getCopy();
            endBounds.translate(moveDelta);
            
            Point newLoc = endBounds.getCenter();
            
            ep.getFigure().setBounds(endBounds);
            //System.err.println("moving from: " + startBounds + " to: " + endBounds);
            
            
            // TODO: is this really necessary?
            // moved: now run the layout manager
            IFigure layoutFig = ep.getFigure().getParent();
            LayoutManager layoutFigLM = layoutFig.getLayoutManager();
            if (layoutFig instanceof Figure) {
                if (layoutFigLM instanceof GraphLayoutManager.SubgraphLayout) {
                    layoutFig = ((AbstractGraphicalEditPart) ep.getRoot().getContents()).getFigure();
                    layoutFigLM = layoutFig.getLayoutManager();
                } 
                if (layoutFigLM instanceof GraphLayoutManager) {
                    ((GraphLayoutManager) layoutFigLM).anchorPart((ArtifactEditPart) ep, newLoc, oldLoc);
                }
                layoutFigLM.layout(layoutFig);
            }
        }
    };

}